package methodCallTreeGen;

import utils.Utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.*;

import static dslProgramGen.util.DslProgramGenUtil.renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName;
import static methodCallTreeGen.MethodCallTreeGenUtil.printRootToSpecifiedTraceNodePath;
import static utils.Utils.RegularExpressionProcess;
import static utils.Utils.getTraceOperationName;

public class MethodCallTreeGen {
    /**
     * 检查trace是否正确，默认需要检查trace的正确性
     */
    public static boolean checkTrace = true;
    /**
     * 如果发现trace有问题，是否忽略有问题的trace，如果不忽略，会抛出trace有问题的运行时异常
     */
    public static boolean ignoreCheckTraceError = true;
    /**
     * 非main线程的 线程信息存储结构
     * key表示threadObjId，即thread对象的hashcode
     * value表示该线程的信息，按顺序为 [线程名称, run方法MethodName, 线程类名]
     */
    public static Map<String, List<String>> threadObjId2ThreadInfoMap = new HashMap<>();
    /**
     * 存储threadInfo的文件名称，文件名固定为 ThreadsInfo.txt
     */
    public static final String threadInfoFileName = "ThreadsInfo.txt";

    /**
     * 存储主类静态初始化traces的文件名称，文件名固定为 staticClinitTracesOfMainClass.txt
     */
    public static final String staticClinitTracesOfMainClassFileName = "staticClinitTracesOfMainClass.txt";

    // 不同线程之间可能调用了相同的函数，为了区分这些函数，对每一个函数进行全局唯一命名，保证不同线程里调用的函数也没有重名的
    // key表示methodName，value表示对这个线程的重名方法进行重命名时，附加的int的开始值
    public static Map<String, Integer> methodStartNumForAThread = new HashMap<>();

    /**
     * 看看当前行是不是这个函数末尾行
     * 如果当前行是Return method1 下一行不是该method1，则认为是其函数末尾
     *
     * @param currentLine 当前行
     * @param nextLine    当前行的下一行
     * @return
     */
    public static boolean isEndOfMethod(String currentLine, String nextLine) {
//        [106.241s][info][gc,heap     ] Del LR H:0x000000000fe618cc, C:Student, T:main, M:java/lang/Object.<init> ()V
//        [106.241s][info][gc,heap     ] Return H:NULL, C:NULL, T:main, M:Student.<init> ()V
        // 获得当前行trace的操作名称
        String currentLineComand = getTraceOperationName(currentLine);
        // 获得下一行trace的操作名称
        String nextLineComand = getTraceOperationName(nextLine);

        // 获得当前行的函数名
        String currentLineMethodName = Utils.RegularExpressionProcess(currentLine, "M:(.*?)$");
        // 获得下一行的函数名
        String nextLineMethodName = Utils.RegularExpressionProcess(nextLine, "M:(.*?)$");
        // 如果当前行的关键字是Return，且当前行与下一行的函数名不同
        if (currentLineComand.startsWith("Return") && !currentLineMethodName.equals(nextLineMethodName)) {
            return true;
        }

        // 如果当前行是method1，下一行也是method1，但当前行关键字是Return，下一行关键字是Add Pr，也认为是函数末尾
        if (currentLineComand.startsWith("Return") && nextLineComand.startsWith("Add PR")
                && currentLineMethodName.equals(nextLineMethodName)) {
            return true;
        }
        return false;
    }

    /**
     * input:从原始trace中过滤掉GC日志 并且 截取出main函数 的trace
     * 处理main函数的trace，生成函数调用树
     *
     * @param srcFilePath 输入文件路径，输入文件内容表示 从原始trace中过滤掉GC日志 并且 截取出main函数 的trace
     * @return 返回函数调用树的根节点，即main函数的节点
     * @throws FileNotFoundException
     */
    @Deprecated
    public static Node generateMethodCallTree1(String srcFilePath) throws FileNotFoundException {
        // main函数所在的methodNode，也是函数调用树的根root
        MethodNode mainMethodNode = null;
        // main函数中的trace的第一部分内容
        TextNode mainTextNode1 = null;
        // 永远指向当前的TextNode
        TextNode currentTextNode = null;


        Scanner scanner = new Scanner(new FileReader(srcFilePath));
        String currentLine; // 表示当前正在处理的行
        String nextLine = scanner.nextLine(); // 表示当前正在处理的行的下一行
        // 表示当前行是不是第一行，如果是的话，进行前面mainMethodNode，mainTextNode1，currentTextNode变量的初始化
        // 因为第一行内容一般是main函数传参，例如 Add PR H:0x0000000002602f45, C:[Ljava/lang/String;, T:main, M:Test.main ([Ljava/lang/String;)V
        boolean isFirstLine = true;
        while (scanner.hasNextLine()) {
            currentLine = nextLine;
            System.out.println(currentLine);
            nextLine = scanner.nextLine();
            // 获得当前行里的methodName
            // [106.241s][info][gc,heap     ] Add PR H:0x0000000038e1d2c4, C:Student, T:main, M:Student.<init> ()V
            String lineMethodName = Utils.RegularExpressionProcess(currentLine, "M:(.*?)$");
            if (isFirstLine) {
                // 初始化函数调用树的root root表示main函数
                mainMethodNode = new MethodNode(NodeType.METHOD, lineMethodName, null);
                mainTextNode1 = new TextNode(NodeType.TEXT, lineMethodName, mainMethodNode);
                mainTextNode1.tracesList.add(currentLine);
                mainMethodNode.contentNodeList.add(mainTextNode1);
                currentTextNode = mainTextNode1;
                isFirstLine = false;
            } else { // 不是第一行内容
                // 如果当前行与currentTextNode在同一个函数
                if (currentTextNode.methodName.equals(lineMethodName)) {
                    currentTextNode.tracesList.add(currentLine);
                    // 看看是不是这个函数末尾了
                    // 如果当前行是method1 下一行不是该method1，则认为是其函数末尾
                    // 如果当前行是method1，下一行也是method1，但当前行关键字是Return，下一行关键字是Add Pr，也认为是函数末尾
                    if (isEndOfMethod(currentLine, nextLine)) {
                        // 如果当前行是函数末尾
                        // 函数返回到其调用函数
                        TextNode textNode = new TextNode(NodeType.TEXT, currentTextNode.parentNode.parentNode.methodName, currentTextNode.parentNode.parentNode);
                        ((MethodNode) currentTextNode.parentNode.parentNode).contentNodeList.add(textNode);
                        currentTextNode = textNode;
                    }
                } else {
                    // 是一个新的method
                    MethodNode newMethod = new MethodNode(NodeType.METHOD, lineMethodName, currentTextNode.parentNode);
                    TextNode textNode = new TextNode(NodeType.TEXT, lineMethodName, newMethod); // 属于newMethod函数内容
                    newMethod.contentNodeList.add(textNode);
                    textNode.tracesList.add(currentLine);

                    ((MethodNode) currentTextNode.parentNode).contentNodeList.add(newMethod);
                    currentTextNode = textNode;
                }
            }
        }
        // 最后一行内容处理
        currentLine = nextLine;
        currentTextNode.tracesList.add(currentLine);
        System.out.println(currentLine);
        return mainMethodNode;
    }

    /**
     * input:从原始trace中过滤掉GC日志 并且 截取出main函数 的trace
     * 处理main函数的trace，生成函数调用树
     *
     * @param srcFilePath 输入文件路径，输入文件内容表示 从原始trace中过滤掉GC日志 并且 截取出main函数 的trace
     * @return 返回函数调用树的根节点，即main函数的节点
     * @throws FileNotFoundException
     */
    public static Node generateMethodCallTree(String srcFilePath) throws FileNotFoundException {
        // main函数所在的methodNode，也是函数调用树的根root
        MethodNode mainMethodNode = null;
        // main函数中的trace的第一部分内容
        TextNode mainTextNode1 = null;
        // 永远指向当前的TextNode
        TextNode currentTextNode = null;

        Scanner scanner = new Scanner(new FileReader(srcFilePath));
        String currentLine; // 表示当前正在处理的行
        // 表示当前行是不是第一行，如果是的话，进行前面mainMethodNode，mainTextNode1，currentTextNode变量的初始化
        // 因为第一行内容一般是main函数传参，例如 Add PR H:0x0000000002602f45, C:[Ljava/lang/String;, T:main, M:Test.main ([Ljava/lang/String;)V
        boolean isFirstLine = true;
        int methodObjParametersNum = 0; // 表示当前正在处理的函数的对象类型的参数数量
        whileTag : while (scanner.hasNextLine()) {
            currentLine = scanner.nextLine();
            // 获得当前行里的methodName
            // [106.241s][info][gc,heap     ] Add PR H:0x0000000038e1d2c4, C:Student, T:main, M:Student.<init> ()V
            String lineMethodName = Utils.RegularExpressionProcess(currentLine, "M:(.*?)$");
            if (isFirstLine) {
                // 初始化函数调用树的root root表示main函数
                mainMethodNode = new MethodNode(NodeType.METHOD, lineMethodName, null);
                mainTextNode1 = new TextNode(NodeType.TEXT, lineMethodName, mainMethodNode);
                mainTextNode1.tracesList.add(currentLine);
                mainMethodNode.contentNodeList.add(mainTextNode1);
                currentTextNode = mainTextNode1;

                // 通过函数的signature和是否静态函数 获得函数传入的对象类型参数数量
                methodObjParametersNum = getObjParametersNumFromMethod(currentLine);
                if (methodObjParametersNum != 0) {
                    methodObjParametersNum--;
                }
                isFirstLine = false;
            } else { // 不是第一行内容
                // 获得当前行的trace操作
                String currentLineComand = getTraceOperationName(currentLine);
                // 获得当前trace的函数名
                String currentLineMethodName = Utils.RegularExpressionProcess(currentLine, "M:(.*?)$");

                switch (currentLineComand) {
                    case "Add PR":
                        // 表示函数参数传递，可能为新调用函数，也可能为当前函数传入的其他参数
                        if (methodObjParametersNum == 0) {
                            // 表示调用一个新的method
                            // 是一个新的method
                            MethodNode newMethod = new MethodNode(NodeType.METHOD, lineMethodName, currentTextNode.parentNode);
                            TextNode textNode = new TextNode(NodeType.TEXT, lineMethodName, newMethod); // 属于newMethod函数内容
                            newMethod.contentNodeList.add(textNode);
                            textNode.tracesList.add(currentLine);

                            ((MethodNode) currentTextNode.parentNode).contentNodeList.add(newMethod);
                            currentTextNode = textNode;

                            // 通过函数的signature和是否静态函数 获得函数传入的对象类型参数数量
                            methodObjParametersNum = getObjParametersNumFromMethod(currentLine);
                            if (methodObjParametersNum != 0) {
                                methodObjParametersNum--;
                            }
                        } else {
                            // 表示之前的method传入的其他对象类型的参数
                            if (checkTrace) {
                                // 检查trace正确性，不正确直接停止程序
                                if (!currentLineMethodName.equals(currentTextNode.methodName)) {
                                    System.err.println("处理Add PR行时，Add PR行的函数名和当前TextNode的函数名不匹配");
                                    System.err.println("当前处理的trace: " + currentLine);
                                    System.err.println("当前TextNode的函数名" + currentTextNode.methodName);
                                    if (ignoreCheckTraceError) {
                                        continue whileTag;
                                    } else {
                                        throw new RuntimeException("Trace有问题");
                                    }

//                                    System.exit(0);
                                }
                                if (!currentTextNode.methodName.equals(currentTextNode.parentNode.methodName)) {
                                    System.err.println("处理Add PR行时，TextNode与MethodNode函数名不匹配");
                                    System.err.println("当前处理的trace: " + currentLine);
                                    System.err.println("当前TextNode的函数名" + currentTextNode.methodName);
                                    System.err.println("当前MethodNode的函数名" + currentTextNode.parentNode.methodName);
                                    if (ignoreCheckTraceError) {
                                        continue whileTag;
                                    } else {
                                        throw new RuntimeException("Trace有问题");
                                    }
//                                    System.exit(0);
                                }
                            }

                            currentTextNode.tracesList.add(currentLine);
                            methodObjParametersNum--;
                        }
                        break;
                    case "Return":
                        // 表示当前处理的函数返回
                        if (checkTrace) {
                            // 检查trace正确性，不正确的话停止程序
                            if (!currentLineMethodName.equals(currentTextNode.methodName)) {
                                System.err.println("处理Return类型的trace语句，处理的trace行的函数名和当前TextNode的函数名不匹配");
                                System.err.println("当前处理的trace: " + currentLine);
                                System.err.println("当前TextNode的函数名" + currentTextNode.methodName);
                                if (ignoreCheckTraceError) {
                                    continue whileTag;
                                } else {
                                    throw new RuntimeException("Trace有问题");
                                }
//                                System.exit(0);
                            }
                            if (!currentTextNode.methodName.equals(currentTextNode.parentNode.methodName)) {
                                System.err.println("处理Return类型的trace语句，TextNode与MethodNode函数名不匹配");
                                System.err.println("当前处理的trace: " + currentLine);
                                System.err.println("当前TextNode的函数名" + currentTextNode.methodName);
                                System.err.println("当前MethodNode的函数名" + currentTextNode.parentNode.methodName);
                                if (ignoreCheckTraceError) {
                                    continue whileTag;
                                } else {
                                    throw new RuntimeException("Trace有问题");
                                }
//                                System.exit(0);
                            }
                        }

                        // 函数返回到其调用函数
                        if (currentTextNode.parentNode.parentNode != null) {
                            currentTextNode.tracesList.add(currentLine);
                            TextNode textNode = new TextNode(NodeType.TEXT, currentTextNode.parentNode.parentNode.methodName, currentTextNode.parentNode.parentNode);
                            ((MethodNode) currentTextNode.parentNode.parentNode).contentNodeList.add(textNode);
                            currentTextNode = textNode;
                        } else {
                            // 如果为null表示是main函数
                            currentTextNode.tracesList.add(currentLine);
                        }
                        break;
                    default:
                        // 其他类型的trace语句，直接加入到当前textNode的traceList中
                        if (checkTrace) {
                            // 检查trace正确性，不正确的话停止程序
                            if (!currentLineMethodName.equals(currentTextNode.methodName)) {
                                System.err.println("处理其他类型的trace语句，处理的trace行的函数名和当前TextNode的函数名不匹配");
                                System.err.println("当前处理的trace: " + currentLine);
                                System.err.println("当前TextNode的函数名" + currentTextNode.methodName);
                                if (ignoreCheckTraceError) {
                                    continue whileTag;
                                } else {
                                    throw new RuntimeException("Trace有问题");
                                }
//                                System.exit(0);
                            }
                            if (!currentTextNode.methodName.equals(currentTextNode.parentNode.methodName)) {
                                System.err.println("处理其他类型的trace语句，TextNode与MethodNode函数名不匹配");
                                System.err.println("当前处理的trace: " + currentLine);
                                System.err.println("当前TextNode的函数名" + currentTextNode.methodName);
                                System.err.println("当前MethodNode的函数名" + currentTextNode.parentNode.methodName);
                                if (ignoreCheckTraceError) {
                                    continue whileTag;
                                } else {
                                    throw new RuntimeException("Trace有问题");
                                }
//                                System.exit(0);
                            }
                        }

                        currentTextNode.tracesList.add(currentLine);
                        break;
                }
            }
        }
        return mainMethodNode;
    }

    /**
     * 通过函数的signature和是否为静态函数获得函数对象类型的参数个数
     * @param addPrLine 函数传参的Add PR行，里面含有函数的signature和是否为静态函数信息
     * @return 函数对象类型的参数个数
     */
    public static int getObjParametersNumFromMethod(String addPrLine) {
        // Add PR H:NULL, C:NULL, T:main, MIS:true, M:Test.AMethod (ILStudent;LCourse;[[D[[[LCourse;[LStudent;)V
        // 首先计算函数signature中含有多少对象类型参数
        String lineMethodName = Utils.RegularExpressionProcess(addPrLine, "M:(.*?)$");
        String methodSignature = lineMethodName.split(" ")[1];
        int objParametersNumFromSignature = computeParameterNumsFromMethodSignature(methodSignature);
        boolean isStaticMethod = isStaticMethod(addPrLine);
        if (isStaticMethod) {
            return objParametersNumFromSignature;
        } else {
            // 非静态函数会多传入一个this参数
            return objParametersNumFromSignature + 1;
        }
    }

    /**
     * 通过trace中的MIS（method is static）字段判断函数是否是静态函数
     * @param addPrLine trace行
     * @return 如果是静态函数返回true
     */
    public static boolean isStaticMethod(String addPrLine) {
        String isStatic = Utils.RegularExpressionProcess(addPrLine, "MIS:(.*?),");
        if ("true".equals(isStatic)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 从函数signature中计算对象类型的参数个数
     * @param methodSignature 函数signature，例如 (ILStudent;LCourse;[[D[[[LCourse;[LStudent;)V
     * @return 对象类型参数个数
     */
    public static int computeParameterNumsFromMethodSignature(String methodSignature) {
        String parametersSignature = Utils.RegularExpressionProcess(methodSignature, "\\((.*?)\\)");
        int objParametersNum = 0; // 表示对象类型参数个数
        if (parametersSignature.length() > 0) {
            // 至少有一个参数
            int charIndex = 0;
            while (charIndex < parametersSignature.length()) {
                char c = parametersSignature.charAt(charIndex);
                if (c == '[') {
                    // 数组类型
                    objParametersNum++;
                    // 可能是多维数组，charIndex前进到第一个不为[的地方
                    while (parametersSignature.charAt(charIndex) == '[') {
                        charIndex++;
                    }
                    // 判断是否是 L即对象类型
                    if (parametersSignature.charAt(charIndex) == 'L') {
                        // 如果数组elementType为对象类型，charIndex前进到 ;
                        while (parametersSignature.charAt(charIndex) != ';') {
                            charIndex++;
                        }
                        // 前进到;后，charIndex++
                        charIndex++;
                    } else {
                        // 为基础数据类型，"B","C","D","F","I","J","S","Z"
                        charIndex++;
                    }
                } else if (c == 'L') {
                    // 为对象类型
                    objParametersNum++;
                    // 如果数组elementType为对象类型，charIndex前进到 ;
                    while (parametersSignature.charAt(charIndex) != ';') {
                        charIndex++;
                    }
                    // 前进到;后，charIndex++
                    charIndex++;
                } else {
                    // 为基础类型
                    charIndex++;
                }

            }
        }
        return objParametersNum;
    }

    /**
     * 打印函数调用树
     *
     * @param root 函数调用树的根节点
     */
    public static void printMethodCallTree(Node root) {
        // 给每一个Node一个编号
        List<Node> nodesList = new ArrayList<>();

        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        nodesList.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                // queue的一层元素
                Node nodeTemp = queue.poll();
                if (nodeTemp.methodName.equals("start")) {
                    System.out.print("(");
                } else if (nodeTemp.methodName.equals("end")) {
                    System.out.print(")               ");
                } else { // 该节点为一个Node
                    if (nodeTemp.nodeType == NodeType.METHOD) {
                        System.out.print("{" + "Num:" + nodesList.indexOf(nodeTemp) + " ParentNum:" + nodesList.indexOf(nodeTemp.parentNode) + " " + nodeTemp.methodName + "} ");
                        // 在每个method的子method序列前后插入Node(start),Node(end)
                        queue.add(new Node(NodeType.TEXT, "start", null));
                        for (Node node : ((MethodNode) nodeTemp).contentNodeList) {
                            if (node.nodeType == NodeType.METHOD) {
                                queue.add(node);
                                nodesList.add(node);
                            }
                        }
                        queue.add(new Node(NodeType.TEXT, "end", null));
                    }
                }
            }
            // 一层打印完之后，换行
            System.out.println();
        }
    }

    /**
     * 移除函数调用树中内容为空的TextNode
     *
     * @param methodNode 函数调用树的函数节点
     */
    public static void removeUselessTextNode(MethodNode methodNode) {
        List<Node> removedTextNodeList = new ArrayList<>(); // 保存内容为空的TextNode
        // 遍历methodNode里面的内容，找出内容为空的TextNode，保存在removedTextNodeList
        for (Node node : methodNode.contentNodeList) {
            if (node.nodeType == NodeType.TEXT) {
                // 如果Node是TextNode
                if (((TextNode) node).tracesList.size() == 0) {
                    // node里面内容为空
                    removedTextNodeList.add(node);
                }
            } else {
                // Node为MethodNode
                removeUselessTextNode((MethodNode) node);
            }
        }
        // 移除掉methodNode中内容为空的TextNode
        for (Node node : removedTextNodeList) {
            methodNode.contentNodeList.remove(node);
        }
    }

    /**
     * 遍历函数调用树，构建SameMethodNameMap <methodName, List<MethodNode>>
     *
     * @param methodNode        当前待处理的MethodNode节点
     * @param sameMethodNameMap <methodName, List<MethodNode>>
     */
    public static void constructSameMethodNameMap(MethodNode methodNode, Map<String, List<MethodNode>> sameMethodNameMap) {
        // 如果methodNode的函数名在sameMethodNameMap的keys中已有
        if (sameMethodNameMap.keySet().contains(methodNode.methodName)) {
            List<MethodNode> sameMethodNameList = sameMethodNameMap.get(methodNode.methodName);
            sameMethodNameList.add(methodNode);
        } else {
            // 如果methodNode的函数名在sameMethodNameMap的keys中没有
            sameMethodNameMap.put(methodNode.methodName, new ArrayList<MethodNode>());
            List<MethodNode> sameMethodNameList = sameMethodNameMap.get(methodNode.methodName);
            sameMethodNameList.add(methodNode);
        }

        // 遍历该methodNode的子函数节点
        for (Node node : methodNode.contentNodeList) {
            if (node.nodeType == NodeType.METHOD) {
                constructSameMethodNameMap((MethodNode) node, sameMethodNameMap);
            }
        }
    }

    /**给函数重命名
     * @param oldMethodName 旧函数名，例如 Test.main ([Ljava/lang/String;)V
     * @return 重命名后的函数名
     */
    public static String constructNewMethodName(String oldMethodName) {
        // 将 空格,(,) 替换为 _
        String replace = oldMethodName.replace(" ", "_").replace("(","_").replace(")","_");
        // 将 / 替换为 _
        String replace1 = replace.replace("/", "_");
        // 将 . 替换为 ___
        String replace2 = replace1.replace(".", "___");
        // 移除;
        String replace3 = replace2.replace(";","");
        // 将 [ 替换为 Arr
        String replace4 = replace3.replace("[", "Arr");
        // 将+ 替换为 _
        return replace4.replace("+","_");
    }

    /**
     * @param methodName 当前处理的methodNode的旧名称
     * @param methodNode 当前处理的methodNode
     * @param sameMethodNameMap <methodName, List<MethodNode>>，在这棵树（一个线程的函数调用树）中独一无二的methodNode也会在该map中，只是List的大小为1
     */
    public static void renameMethodNodeNameNotThreadRunMethod(String methodName, MethodNode methodNode, Map<String, List<MethodNode>> sameMethodNameMap) {
        // 如果该methodNode的名字在其他线程没有
        if (methodStartNumForAThread.get(methodName) == null) {
            methodStartNumForAThread.put(methodName, 0);
        }

        String oldMethodName = methodName;
        String newMethodName = constructNewMethodName(oldMethodName);
        // 如果是类的静态方法的话，要重命名去掉"<"与">"，类的静态方法 例如Student___<clinit>___V
        if (newMethodName.contains("___<clinit>")) {
            // 更改函数名 Student___<clinit>___V 为 Student_____clinit_____V
            newMethodName = newMethodName.replace("<", "__").replace(">", "__");
        }
        // 在newMethodName后拼接 "_vi"，i的值计算方法 methodStartNumForAThread.get(oldMethodName) + sameMethodNameMap.get(oldMethodName).indexOf(methodNode)
        int oldMethodStartIndex = methodStartNumForAThread.get(oldMethodName);
        newMethodName += "_v" + (oldMethodStartIndex + sameMethodNameMap.get(oldMethodName).indexOf(methodNode));
        methodNode.newMethodName = newMethodName;
    }
    /**
     * 重命名methodNode的名称，保存在MethodNode的newMethodName字段中
     * 对于每一个methodNode，首先判断其是不是某一个线程的run方法
     * 如果是某个线程的run方法，命名方式为 threadName + "___" + constructNewMethodName(oldMethodName);
     * 如果不是某个线程的run方法，命名方法具体参考函数 renameMethodNodeNameNotThreadRunMethod，会在函数名后面+ _vi(例如_v0,_v1)
     *
     * @param methodNode        当前处理的methodNode
     * @param sameMethodNameMap <methodName, List<MethodNode>>，在这棵树中（一个线程的函数调用树）独一无二的methodNode也会在该map中，只是List的大小为1
     */
    public static void renameMethodNodeName(MethodNode methodNode, Map<String, List<MethodNode>> sameMethodNameMap) {
        String methodName = methodNode.methodName;
        // 对线程的run方法命名单独处理
        // [25.776s][info][gc,heap     ] Add PR H:0x0000000076462bc3, C:MyThread, T:Thread-0, M:MyThread.run ()V
        // 获得该MethodNode的第一行Add PR内容
        String firstAddPrLine = ((TextNode) (methodNode.contentNodeList.get(0))).tracesList.get(0);
        // 获得其hid
        String threadObjId = RegularExpressionProcess(firstAddPrLine, "H:(.*?),");

        // 看是不是某一个线程的run方法，run方法单独命名
        if (threadObjId2ThreadInfoMap.keySet().contains(threadObjId)) {
            if (isThreadRunMethod(firstAddPrLine, threadObjId2ThreadInfoMap.get(threadObjId))) {
                // 是某一个线程的run方法
                // 获得线程名称
                String threadName = RegularExpressionProcess(firstAddPrLine, "T:(.*?),");
                threadName = renameThreadName(threadName);
                String oldMethodName = methodName;
                // 新run方法的命名为threadName + "___" + constructNewMethodName(oldMethodName);
                String newMethodName = threadName + "___" + constructNewMethodName(oldMethodName);
                methodNode.newMethodName = newMethodName;
            } else {
                renameMethodNodeNameNotThreadRunMethod(methodName, methodNode, sameMethodNameMap);
            }

        } else {
            renameMethodNodeNameNotThreadRunMethod(methodName, methodNode, sameMethodNameMap);
        }


        // 遍历该methodNode的子函数节点
        for (Node node : methodNode.contentNodeList) {
            if (node.nodeType == NodeType.METHOD) {
                renameMethodNodeName((MethodNode) node, sameMethodNameMap);
            }
        }
    }

    /**
     * 重命名线程名称
     * @param oldThreadName 例如Thread-0
     * @return 重命名后的线程名 例如Thread_0
     */
    public static String renameThreadName(String oldThreadName) {
        String newThreadName = oldThreadName.replace("-", "_");
        return newThreadName;
    }

    /**
     * 判断是不是一个线程的run方法
     * @param firstAddPrLine 获得该MethodNode的第一行Add PR内容，例如
     * [25.776s][info][gc,heap     ] Add PR H:0x0000000076462bc3, C:MyThread, T:Thread-0, M:MyThread.run ()V
     * @param threadInfoList 表示线程的信息，按顺序为 [线程名称, run方法MethodName, 线程类名]
     * @return
     */
    public static boolean isThreadRunMethod(String firstAddPrLine, List<String> threadInfoList) {
        // 获得线程名称
        String threadName = RegularExpressionProcess(firstAddPrLine, "T:(.*?),");
        // 获得run方法的methodName
        String runMethodName = RegularExpressionProcess(firstAddPrLine, "M:(.*?)$");
        // 获得其线程类名
        String threadClassName = RegularExpressionProcess(firstAddPrLine, "C:(.*?),");
        threadClassName = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(threadClassName);
        if (threadInfoList.get(0).equals(threadName) &&
        threadInfoList.get(1).equals(runMethodName) &&
        threadInfoList.get(2).equals(threadClassName)) {
            return true;
        }
        return false;
    }

    /**
     * 产生经过重命名后的函数调用树
     *
     * @param srcFilePath 源文件路径
     * @return 函数调用树的根节点
     * @throws FileNotFoundException
     */
    public static Node generateRenamedMethodCallTree(String srcFilePath) throws FileNotFoundException {
        Node rootNode = generateMethodCallTree(srcFilePath);
        // 生成的函数调用树可能有一些TextNode里面没有任何内容，移除掉内容为空的TextNode
        removeUselessTextNode((MethodNode) rootNode);
        // 遍历函数调用树，统计相同函数名的函数，并且给他们重新命名
        Map<String, List<MethodNode>> sameMethodNameMap = new HashMap<>(); // <methodName, List<MethodNode>>
        constructSameMethodNameMap((MethodNode) rootNode, sameMethodNameMap);
        // 重命名MethodNode节点
        renameMethodNodeName((MethodNode) rootNode, sameMethodNameMap);
        // 处理一个thread后，更新methodStartNumForAThread
        for (String methodNameTemp : sameMethodNameMap.keySet()) {
            // methodStartNumForAThread.get(methodNameTemp)不为null，因为在renameMethodNodeName函数已经初始化过
            // 为null的情况只有thread的run方法，而run方法是单独命名的
            if (methodStartNumForAThread.get(methodNameTemp) != null) {
                int cntNum = methodStartNumForAThread.get(methodNameTemp);
                cntNum += sameMethodNameMap.get(methodNameTemp).size();
                methodStartNumForAThread.put(methodNameTemp, cntNum);
            }
        }

        return rootNode;
    }

    /**判断函数调用树中的一个MethodNode是不是JVM内部的trace
     * @param methodNode 待判断的MethodNode
     * @return 如果该MethodNode是Jvm内部产生的，则返回true
     */
    public static boolean isJvmMethodNode(MethodNode methodNode) {
        // 第一个特点 methodName为 java/lang/ClassLoader.loadClass (Ljava/lang/String;)Ljava/lang/Class;
        if (methodNode.methodName.contains("java/lang/ClassLoader.loadClass")) {
            return true;
        }
        // 第二个特点 methodName为 java/lang/Thread.<init> ()V
        // 假设用户定义了一个MyThread类继承自Thread类，当调用MyThread的构造函数时，JVM底层会额外调用java/lang/Thread.<init>函数，而java/lang/Thread.<init>是JVM自己调用的，与用户代码无关
        if (methodNode.methodName.contains("java/lang/Thread.<init> ()V")) {
            return true;
        }
        // 第三个特点 methodName为 java/lang/ThreadGroup.add (Ljava/lang/Thread;)V
        // 假设用户定义了一个MyThread类继承自Thread类，一个MyThread的对象为mythread，当调用mythread.start()方法时，jvm底层会调用java/lang/ThreadGroup.add，同样与用户代码无关
        if (methodNode.methodName.contains("java/lang/ThreadGroup.add (Ljava/lang/Thread;)V")) {
            return true;
        }
        // 第四个特点 methodName为 java/lang/Thread.start0 ()V 同理特点三
        if (methodNode.methodName.contains("java/lang/Thread.start0 ()V")) {
            return true;
        }
        // valueOf注释原因是未获取到静态数组cache的初始化，它是返回值的基础
        if (methodNode.methodName.contains("java/lang/String.valueOf (I)Ljava/lang/String;")) {
            return true;
        }
        if (methodNode.methodName.contains("java/lang/String.equals (Ljava/lang/Object;)Z")) {
            return true;
        }
        //
        if (methodNode.methodName.contains("java/lang/Integer.valueOf (I)Ljava/lang/Integer;")) {
            return true;
        }
        //
        if (methodNode.methodName.contains("java/lang/Double.valueOf (D)Ljava/lang/Double;")) {
            return true;
        }
        if (methodNode.methodName.contains("java/lang/Shutdown.add (IZLjava/lang/Runnable;)V")) {
            return true;
        }
        if (methodNode.methodName.contains("jdk/internal/loader/BuiltinClassLoader.findResourcesOnClassPath (Ljava/lang/String;)Ljava/util/Enumeration;")) {
            return true;
        }
        if (methodNode.methodName.contains("java/util/logging/Logger.getLogger (Ljava/lang/String;)Ljava/util/logging/Logger;")) {
            return true;
        }
        return false;
    }

    /**从函数调用树中移除JVM内部产生的MethodNode
     * @param methodNode 当前处理的methodNode
     */
    public static void removeJvmMethodNode(MethodNode methodNode, String dstFilePath) {
        List<Node> removedJvmMethodNodeList = new ArrayList<>();
        // 遍历该methodNode的子函数节点
        for (Node node : methodNode.contentNodeList) {
            if (node.nodeType == NodeType.METHOD) {
                if (isJvmMethodNode((MethodNode) node)) {
                    // 如果是jvm内部调用的函数，则不访问其子节点，将其添加到removedJvmMethodNodeList中
                    removedJvmMethodNodeList.add(node);
                } else {
                    removeJvmMethodNode((MethodNode) node, dstFilePath);
                }
            }
        }
        // 从函数调用树中移除掉JVM内部调用的MethodNode节点
        // 把JVM内部调用的MethodNode节点的parent设置为null
        for (Node node : removedJvmMethodNodeList) {
            // 把移除的Traces内容输出到文件中
            writeMethodCallTreeTraces(node, dstFilePath);
            methodNode.contentNodeList.remove(node);
            node.parentNode = null;
        }
    }

    public static void writeMethodCallTreeTraces(Node node, String dstFilePath) {
        if (node.nodeType == NodeType.METHOD) {
            // 如果是MethodNode
            for (Node subNode : ((MethodNode)node).contentNodeList) {
                writeMethodCallTreeTraces(subNode, dstFilePath);
            }
        } else {
            // 是TextNode
            for (String str : ((TextNode)node).tracesList) {
                Utils.writeLineToDstFile(str, dstFilePath);
            }
        }
    }

    /** 合并多个相邻的TextNode为一个TextNode，产生多个相邻TextNode主要原因是移除JVM的MethodNode过程中，原本不相邻的两个TextNode变得相邻了
     * @param methodNode 待处理的MethodNode
     */
    public static void mergeContiguousTextNodes(MethodNode methodNode) {
        // 待移除的TextNode列表
        List<Node> removedNodeList = new ArrayList<>();
        List<Node> methodContentNodeList = methodNode.contentNodeList;
        int index = 0; // 遍历methodContentNodeList的下标
        int startTextNodeIndex = -1; // 表示连续TextNodes开始的那个TextNode下标

        while (index < methodContentNodeList.size()) {
            Node node = methodContentNodeList.get(index);
            if (node.nodeType == NodeType.TEXT) {
                // 如果node是TextNode
                if (startTextNodeIndex == -1) {
                    // 如果是连续TextNode的开始
                    startTextNodeIndex = index;
                } else {
                    // 该node为连续TextNode中间的部分
                    // 获得连续的TextNode开始的TextNode的traceList
                    List<String> startTextNodeTracesList = ((TextNode)(methodContentNodeList.get(startTextNodeIndex))).tracesList;
                    // 将该node的traceList加入到startTextNodeTracesList中
                    startTextNodeTracesList.addAll(((TextNode)node).tracesList);
                    removedNodeList.add(node);
                }
            } else {
                // node是MethodNode
                startTextNodeIndex = -1;
                mergeContiguousTextNodes((MethodNode) node);
            }
            index++;
        }

        // 移除多余的TextNode
        for (Node node : removedNodeList) {
            methodContentNodeList.remove(node);
        }
    }


    public static void main(String[] args) throws FileNotFoundException {
        // 这里不检查trace的正确性，debug用
        checkTrace = false;
        ignoreCheckTraceError = true;
        //        String srcFilePath = "methodTraces.txt";
//        int num = 14;
//        String srcFilePath = "src/test/resources/dslProgramGenForComplexMop/filteredTraces/"+num+"/main.txt";
        String srcFilePath = "src/test/resources/dslProgramGenForComplexMop/filteredTraces/14/main.txt";
//        String srcFilePath = "src/test/resources/dslProgramGenForBaseMop/inputRaw/rawRefinedTraces_1.txt";
        String dstFilePath = "src/main/java/methodCallTreeGen/a.txt";
        // 判断dstFilePath文件是否存在，如果存在，则删除
        File file = new File(dstFilePath);
        if (file.exists()) {
            file.delete();
        }

        long startTime = System.currentTimeMillis();

        Node rootNode = generateRenamedMethodCallTree(srcFilePath);
        // 生成的函数调用树中含有JVM内部的trace，JVM内部的trace与应用程序内存操作无关，移除JVM内部trace
        removeJvmMethodNode((MethodNode) rootNode, "src/main/java/methodCallTreeGen/removed.txt");
        // 合并多个相邻的TextNode为一个TextNode
        mergeContiguousTextNodes((MethodNode) rootNode);
        // 遍历函数调用树，打印其trace到文件中
        writeMethodCallTreeTraces(rootNode, dstFilePath);

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        System.out.println("用时: " + executionTime / 1000.0 / 3600.0 + "小时");

        // 打印函数调用树
//        System.out.println();
//        System.out.println();
//        System.out.println("打印函数调用树");
//        printMethodCallTree(rootNode);

        // 打印root到目标trace的TextNode的path
//        String targetTrace = "[34.969s][info][gc,heap     ] Add PR H:0x0000000062041e5f, C:jdk/internal/loader/ClassLoaders$BootClassLoader, T:main, MIS:false, M:jdk/internal/loader/BuiltinClassLoader.findResource (Ljava/lang/String;)Ljava/net/URL;";
//        printRootToSpecifiedTraceNodePath(targetTrace, (MethodNode) rootNode);
    }


}
